/*
 *
 *  * Copyright (c) 2016. For Intelligent Group.
 *
 */

package com.intelligent.ispc.chart.grr;

import com.intelligent.ispc.common.utils.MathUtils;
import com.intelligent.ispc.foundation.chart.AbstractChart;
import com.intelligent.ispc.foundation.chart.axis.FixedNumberAxis;
import com.intelligent.ispc.foundation.chart.util.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.labels.ItemLabelAnchor;
import org.jfree.chart.labels.ItemLabelPosition;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.BarRenderer;
import org.jfree.data.Range;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.ui.TextAnchor;

import java.awt.*;
import java.util.ArrayList;

/**
 * Created by cherry on 2016/8/31.
 * Modified by QiangChen on 2016/11/7.
 */
public class ComponentsChart extends AbstractChart {

    private DefaultCategoryDataset dataset;
    private JFreeChart localChart;
    private double[][] sourceData;
    private String chartName;
    private boolean isDataValid;

    /**
     * Constructor.
     *
     * @param componentValue value.
     */
    public ComponentsChart(double[][] componentValue) {
        this.isDataValid = true;
        this.chartName = "Components of Variation";
        this.sourceData = componentValue;
        this.dataset = createDataset();
    }

    /**
     * Create dataset for Components of Variation Chart, and the abnormal data is processed as necessary.
     *
     * @return return the dataset for plotting.
     */
    public DefaultCategoryDataset createDataset() {

        if (sourceData == null) {
            isDataValid = false;
        } else {
            String[] category = new String[]{"%Contribution", "%Variation", "%Tolerance"};
            String[] xLabel = new String[]{"Repeatability", "Reproducibility", "Gage R&R", "Parts"};

            dataset = new DefaultCategoryDataset();
            for (int i = 0; i < sourceData.length; i++) {
                for (int j = 0; j < sourceData[i].length; j++) {
                    if (Double.isNaN(sourceData[i][j])) {
                        continue;
                    }
                    dataset.setValue(sourceData[i][j], category[i], xLabel[j]);
                }
            }
            return dataset;
        }
        return null;
    }

    @Override
    protected JFreeChart buildChart() {

        if (!isDataValid) {
            return this.blankChart(chartName);
        }
        localChart = ChartFactory.createBarChart("Components of Variation", "", "", dataset, PlotOrientation.VERTICAL, true, true, false);
        CategoryPlot localCategoryPlot = (CategoryPlot) localChart.getPlot();
        localCategoryPlot.setNoDataMessage("NO DATA!");
        localCategoryPlot.setDomainGridlinesVisible(true);
        localCategoryPlot.setRangeCrosshairVisible(true);
        localCategoryPlot.setRangeCrosshairPaint(ChartColor.BLUE);

        BarRenderer localBarRenderer = (BarRenderer) localCategoryPlot.getRenderer();
        localBarRenderer.setDrawBarOutline(false);
        final double itemMargin = 0.1;
        localBarRenderer.setItemMargin(itemMargin);
        localBarRenderer.setBaseItemLabelGenerator(new LabelGenerator(this.getDigNum()));
        localBarRenderer.setBaseItemLabelsVisible(true);
        final int fontSize = 10;
        localBarRenderer.setBaseItemLabelFont(new Font("", Font.CENTER_BASELINE, fontSize));
        localBarRenderer.setBaseItemLabelPaint(ChartColor.BLACK);
        localBarRenderer.setBasePositiveItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER, TextAnchor.BOTTOM_CENTER, 0D));
        localBarRenderer.setPositiveItemLabelPositionFallback(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER, TextAnchor.BOTTOM_CENTER, 0D));
        localBarRenderer.setBaseNegativeItemLabelPosition(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER, TextAnchor.TOP_CENTER, 0D));
        localBarRenderer.setNegativeItemLabelPositionFallback(new ItemLabelPosition(ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER, TextAnchor.TOP_CENTER, 0D));

        localBarRenderer.setDrawBarOutline(false);
        localBarRenderer.setShadowVisible(false);

        localBarRenderer.setSeriesPaint(0, ChartColor.BLUE);
        localBarRenderer.setSeriesPaint(1, ChartColor.GREEN);
        localBarRenderer.setSeriesPaint(2, ChartColor.RED);

        FixedNumberAxis axis = new FixedNumberAxis();
        java.util.List<Double> data = new ArrayList<Double>();
        for (int i = 0; i < sourceData.length; i++) {
            for (int j = 0; j < sourceData[i].length; j++) {
                if (!Double.isNaN(sourceData[i][j])) {
                    data.add(sourceData[i][j]);
                }
            }
        }

        //The maximum and minimum values are calculated to limit the range of Y coordinates.
        double min = 0;
        double max = 0;

        if (data != null && data.size() != 0) {
            min = MathUtils.findMin(data);
            max = MathUtils.findMax(data);
        }

        if (max != min) {
            final double factor = 0.20;
            double reserve = (max - min) * factor;
            if (min < 0) {
                reserve = (max + min) * factor;
            }

            if (min == 0) {
                axis.setR(new Range(min, max + reserve));
            } else {
                axis.setR(new Range(min - reserve, max + reserve));
            }
            localCategoryPlot.setRangeAxis(axis);
        }

        localBarRenderer.setLegendItemToolTipGenerator(new StandardCategorySeriesLabelGenerator("Tooltip: {0}"));
        CategoryAxis localCategoryAxis = localCategoryPlot.getDomainAxis();
        localCategoryAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(0.0D));
        return localChart;
    }

    /*
    * LabelGenerator is used to display data in percentage format.
    * */
    private static class LabelGenerator extends StandardCategoryItemLabelGenerator {

        private int grrDigit;

        LabelGenerator(int grrDigit) {
            this.grrDigit = grrDigit;
        }

        /**
         * The generateLabel function is used to generate the percentage format of the data.
         *
         * @param paramCategoryDataset is the dataset.
         * @param paramInt1            is the row.
         * @param paramInt2            is the column.
         * @return Return the percentage format of the data.
         */
        public String generateLabel(CategoryDataset paramCategoryDataset, int paramInt1, int paramInt2) {
            Number value = paramCategoryDataset.getValue(paramInt1, paramInt2);

            if (grrDigit > 2) {
                return String.format("%." + (grrDigit - 2) + "f", value) + "%";
            } else {
                return String.format("%." + 0 + "f", value) + "%";
            }
        }
    }
}
